function Jscheme() {
    /**
     * prog -> expressions
     * expressions -> expression expressions
     *                | expression 
     *                |
     * expression  -> (expression expressions)
     *                |'()
     *                |(lambda (identifys) expression)
     *                |(if expression expression expression)
     *                |(define identify expression)
     *                |atom
     *                |'expression
     * identifys   -> identify identifys
     *                | identify
     *                | 
     * atom        -> number|string|char|bool|identify
     * number      -> (+|-)\d+|(+|-)\d+\.\d+| #b[01]+ |#o[0-7]+ |#d[0-9]+ |#x[0-9a-fA-F]+ 
     * string      -> ""
     * char        -> #\.+
     * bool        -> #t|#f
     */
    this.string_generator = function* (code) {
        for (var index = 0; index < code.length; ++index) {
            var back = yield code[index];
            if (back) {
                index -= back + 1;
            }
        }
    }
    this.compare = function (code, s) {

    }
    this.read_until = function (code) {
        var r = "";
        while (true) {
            var v = code.next();
            if (v.done) {
                break;
            }
            if (/\s|\(|\)/.test(v.value)) {
                code.next(1);
                break;
            }
            r += v.value;
        }
        return r;
    }
    this.parse_string = function (code) {
        var ret = { t: 'string', val: '' };
        while (true) {
            var v = code.next();
            if (v.done) {
                break;
            }
            if (v.value == '"') {
                return ret;
            }
            if (v.value == '\\') {
                v = code.next();
                if (v.done) {
                    break;
                }
                if (v.value == '\\') {
                    ret.val += '\\';
                } else if (v.value == 'n') {
                    ret.val += '\n';
                } else if (v.value == 'r') {
                    ret.val += '\r';
                } else if (v.value == 't') {
                    ret.val += '\t';
                } else if (v.value == '"') {
                    ret.val += '"';
                } else {
                    break;
                }
            }
            ret.val += v.value;
        }
        throw new Error('error string.');
    }

    this.parse_number = function (code) {
        var s = this.read_until(code);
        if (/(\+|-)?\d+/.test(s)) {
            return { t: 'integer', val: parseInt(s) };
        }
        if (/(\+|-)?\d+((\.\d+)|(\+|-)?e\d+)/.test(s)) {
            return { t: 'double', val: parseFloat(s) };
        }
        throw new Error('error number.');
    }
    this.parse_identify = function (code) {
        var ret = { t: 'identify', val: false };
        var s = this.read_until(code);
        if (s.length > 0) {
            ret.val = s;
            return ret;
        }
        throw new Error('error identify.');
    }
    this.parse_char = function (code) {
        var ret = { t: 'char', val: false };
        var t = code.next();
        if (t.value == '#') {
            t = code.next();
            if (t.value == '\\') {
                var s = this.read_until();
                if (s == 'newline') {
                    ret.val = "\n";
                    return ret;
                }
                if (s == 'space') {
                    ret.val = " ";
                    return ret;
                }
                if (s.length == 1) {
                    ret.val = s;
                    return ret;
                }
            }
        }
        throw new Error('error char.');
    }
    this.parse_bool = function (code) {
        var ret = { t: 'bool', val: false };
        var t = code.next();
        if (t.value == '#') {
            t = code.next();
            if (t.value == 't' || t.value == 'T') {
                ret.val = true;
                return ret;
            }
            if (t.value == 'f' || t.value == 'F') {
                ret.val = false;
                return ret;
            }
        }
        throw new Error('error bool.');
    }
    this.parse_form = function (code) {
        var ret = { t: 'form', val: [] };
        code.next();
        while (true) {
            var t = code.next();
            if (t.done) {
                break;
            }
            if (t.value == ')') {
                return ret;
            }
            if (/\s+/.test(t.value)) {
                continue;
            }
            code.next(1);
            ret.val.push(this.parse_expression(code));
        }
        throw new Error('error form.');
    }
    this.parse_expression = function (code) {
        var ret = { t: 'expression', val: null };
        var t = code.next();
        switch (t.value) {
            case '#':
                t = code.next();
                if (!t.done) {
                    if (t.value == '\\') {
                        code.next(2);
                        //ret.val = this.parse_char(code);
                        ret = this.parse_char(code);
                        return ret;
                    } else {
                        code.next(2);
                        //ret.val = this.parse_bool(code);
                        ret = this.parse_bool(code);
                        return ret;
                    }
                }
                break;
            case '\'':
                //var ret = { t: 'expression', val: { t: 'quote', val: this.parse_expression(code) } };
                t = code.next();
                var t1 = code.next();
                if (t.value == '(' && t1.value == ')') {
                    return { t: 'nil', val: null };
                }
                code.next(2);
                var ret = { t: 'quote', val: this.parse_expression(code) }
                return ret;
            case '(':
                code.next(1);
                //ret.val = this.parse_form(code);
                ret = this.parse_form(code);
                return ret;
            case '"':
                //ret.val = this.parse_string(code);
                ret = this.parse_string(code);
                return ret;
            default:
                if (t.value == '+' || t.value == '-') {
                    t = code.next();
                    if (!t.done) {
                        if (t.value >= '0' && t.value <= '9') {
                            code.next(2);
                            //ret.val = this.parse_number(code);
                            ret = this.parse_number(code);
                            return ret;
                        } else {
                            code.next(1);
                        }
                    }
                } else if (t.value >= '0' && t.value <= '9') {
                    code.next(1);
                    //ret.val = this.parse_number(code);
                    ret = this.parse_number(code);
                    return ret;
                } else if (/\s+/.test(t.value)) {

                }
                code.next(1);
                //ret.val = this.parse_identify(code);
                ret = this.parse_identify(code);
                return ret;

        }
        throw new Error('error expression.');
    }
    this.parse_expressions = function (code) {
        var ret = { t: 'expressions', val: [] };
        ret.val.push(this.parse_expression(code));
        var r = code.next();
        if (!r.done) {
            code.next(1);
            var tmp = this.parse_expressions(code);
            ret.val = ret.val.concat(tmp.val);
        }
        return ret;
    }
    this.parse = function (code) {
        var gen = this.string_generator(code);
        return this.parse_expressions(gen)
    }
    this.let_to_lambda = function (ast) {
        //(let ((x 1)) (+ x 1)) -> ((lambda (x) (+ x 1)) 1)
        var ret = { t: 'form', val: [] };
        ret.val.push({
            t: 'form',
            val: [{ t: 'identify', val: 'lambda' }]
        });
        var params = [];
        var values = [];
        for (var idx in ast.val[1].val) {
            params.push(ast.val[1].val[idx].val[0]);
            values.push(ast.val[1].val[idx].val[1]);
        }
        ret.val[0].val.push({ t: 'form', val: params });
        ret.val[0].val.push(this.optimize(ast.val[2]));
        ret.val = ret.val.concat(values);
        return ret;
    }
    this.optimize = function (ast) {
        switch (ast.t) {
            case 'expressions':
                for (var idx in ast.val) {
                    ast.val[idx] = this.optimize(ast.val[idx]);
                }
                return ast;
                break;
            case 'form':
                if (ast.val[0].t == 'identify') {
                    if (ast.val[0].val == 'let') {
                        return this.let_to_lambda(ast)
                    }
                    if (ast.val[0].val == 'begin') {
                        var newnode = { t: 'expressions', val: [] };
                        for (var idx = 1; idx < ast.val.length; ++idx) {
                            newnode.val.push(this.optimize(ast.val[idx]));
                        }
                        return newnode;
                    }
                }
                for (var idx in ast.val) {
                    ast.val[idx] = this.optimize(ast.val[idx]);
                }
                return ast;
            case 'quote':
                return ast;
            default:
                return ast;
        }
    }
    this.tailcall = function (ast, istail = false) {
        switch (ast.t) {
            case 'expressions':
                for (var idx in ast.val) {
                    if (idx == ast.val.length - 1) {
                        ast.val[idx] = this.tailcall(ast.val[idx], istail);
                    } else {
                        ast.val[idx] = this.tailcall(ast.val[idx], false);
                    }

                }
                return ast;
                break;
            case 'form':
                if (ast.val[0].t == 'identify') {
                    if (ast.val[0].val == 'lambda') {
                        ast.istail = istail;
                        ast.val[2] = this.tailcall(ast.val[2], true)
                        return ast;
                    }
                    if (ast.val[0].val == 'if') {
                        ast.val[1] = this.tailcall(ast.val[1], false)
                        ast.val[2] = this.tailcall(ast.val[2], istail)
                        ast.val[3] = this.tailcall(ast.val[3], istail)
                        return ast;
                    }
                }
                //function call
                ast.istail = istail;
                for (var idx in ast.val) {
                    ast.val[idx] = this.tailcall(ast.val[idx], false);

                }
                return ast;
            case 'quote':
                ast.istail = istail
                return ast;
            default:
                ast.istail = istail
                return ast;
        }
    }
    this.compile_to_secd = function (ast) {
        function lookup(v, env) {
            var tmp = env;
            var m = 0;
            while (tmp) {
                if (tmp['val'][v] !== undefined) {
                    return [m, tmp.val[v]]
                }
                ++m;
                tmp = tmp['up'];
            }
            return undefined;
        }
        function setenv(v, env) {
            env.val[v] = Object.keys(env.val).length;
        }

        var glob = { val: {} };
        function translate(ast, env = { val: {} }) {
            switch (ast.t) {
                case 'expressions':
                    var secd = [];
                    for (var idx in ast.val) {
                        secd = secd.concat(translate(ast.val[idx], env));
                    }
                    return secd;
                    break;
                case 'form':
                    var secd = [];
                    if (ast.val[0].t == 'identify') {
                        if (ast.val[0].val == 'lambda') {
                            secd.push('LDF');
                            var e = { up: env, val: {} }
                            for (var idx in ast.val[1].val) {
                                setenv(ast.val[1].val[idx].val, e);
                            }
                            secd.push(translate(ast.val[2], e).concat(['RTN']))
                            return secd;
                        } else if (ast.val[0].val == 'if') {
                            secd = secd.concat(translate(ast.val[1], env));
                            secd.push('SEL')
                            secd.push(translate(ast.val[2], env).concat(['JOIN']))
                            secd.push(translate(ast.val[3], env).concat(['JOIN']))
                            return secd;
                        } else if (ast.val[0].val == 'define') {
                            secd.push('LDC')
                            secd.push(ast.val[1].val)
                            secd.push(translate(ast.val[2], env))
                            secd.push('DEF')
                            return secd
                        } else {
                            secd.push('NIL')
                            for (var idx = 1; idx < ast.val.length; ++idx) {
                                secd = secd.concat(translate(ast.val[idx], env));
                                secd.push('CONS')
                            }
                            secd = secd.concat(translate(ast.val[0], env));
                            secd.push('AP')
                            return secd
                        }
                    } else {
                        secd.push('NIL')
                        for (var idx = 1; idx < ast.val.length; ++idx) {
                            secd = secd.concat(translate(ast.val[idx], env));
                            secd.push('CONS')
                        }
                        secd = secd.concat(translate(ast.val[0], env));
                        secd.push('AP')
                        return secd
                    }
                    break;
                case 'identify':
                    var seek = lookup(ast.val, env);
                    var secd = [];
                    if (seek) {
                        secd.push('LD')
                        secd.push(seek)
                    } else {
                        secd.push('LDN')
                        secd.push(ast.val)
                    }
                    return secd;
                    break;
                case 'quote':
                    break;
                default:
                    return ['LDC', ast.val];
            }
        }
        return translate(ast);

    }
    this.compile_to_js = function (ast) {
        function lookup(v, env) {
            var tmp = env;
            var m = 0;
            var n = 0;
            var r = null;
            while (tmp) {
                if (tmp['val'][v] !== undefined) {
                    n = m
                    r = tmp.val[v]
                }
                ++m;
                tmp = tmp['up'];
            }
            if (r !== null) {
                return [m - n, r]
            }
            return undefined;
        }
        function setenv(v, env) {
            env.val[v] = Object.keys(env.val).length;
        }

        var glob = { val: {} };
        function translate(ast, env = { val: {} }) {
            switch (ast.t) {
                case 'expressions':
                    var secd = '';
                    for (var idx in ast.val) {
                        secd += translate(ast.val[idx], env) + ";\n";
                    }
                    return secd;
                    break;
                case 'form':
                    var secd = '';
                    if (ast.val[0].t == 'identify') {
                        if (ast.val[0].val == 'lambda') {
                            var e = { up: env, val: {} }
                            var args = '';
                            for (var idx in ast.val[1].val) {
                                setenv(ast.val[1].val[idx].val, e);
                                var t = lookup(ast.val[1].val[idx].val, e);
                                args += '_v_' + t[0] + "_" + t[1];
                                if (idx != ast.val[1].val.length - 1) {
                                    args += ","
                                }
                            }
                            if (ast.istail) {
                                secd += 'return '
                            }
                            secd += 'function (' + args + '){';
                            secd += translate(ast.val[2], e)
                            secd += '}';
                            return secd;
                        } else if (ast.val[0].val == 'if') {
                            secd += 'var _cond=' + translate(ast.val[1], env)+";";
                            secd += 'if(_cond){'
                            secd += translate(ast.val[2], env)
                            secd += '}else{';
                            secd += translate(ast.val[3], env)
                            secd += '}';
                            return secd;
                        } else if (ast.val[0].val == 'define') {
                            secd.push('LDC')
                            secd.push(ast.val[1].val)
                            secd.push(translate(ast.val[2], env))
                            secd.push('DEF')
                            return secd
                        } else if (ast.val[0].val == '+' || ast.val[0].val == '-' || ast.val[0].val == '/' || ast.val[0].val == '*') {
                            if (ast.istail) {
                                secd += 'return '
                            }
                            for (var idx = 1; idx < ast.val.length; ++idx) {
                                secd += '(' + translate(ast.val[idx], env) + ')';
                                if (idx != ast.val.length - 1) {
                                    secd += ast.val[0].val
                                }
                            }
                            return secd
                        } else if (ast.val[0].val == '>' || ast.val[0].val == '<' || ast.val[0].val == '==' || ast.val[0].val == '>=' || ast.val[0].val == '<=') {
                            if (ast.istail) {
                                secd += 'return '
                            }
                            for (var idx = 1; idx < ast.val.length; ++idx) {
                                secd += '(' + translate(ast.val[idx], env) + ')';
                                if (idx != ast.val.length - 1) {
                                    secd += ast.val[0].val
                                }
                            }
                            return secd
                        } else {
                            var seek = lookup(ast.val[0].val, env);
                            var args = '';
                            for (var idx = 1; idx < ast.val.length; ++idx) {
                                secd += '_p_' + idx + "=" + translate(ast.val[idx], env) + ';';
                                args += '_p_' + idx
                                if (idx != ast.val.length - 1) {
                                    args += ","
                                }
                            }
                            if (ast.istail) {
                                secd += 'return '
                            }
                            if(!seek){
                                secd += ast.val[0].val+'(' + args + ');';
                            }else{
                                secd += 'var _func=' + translate(ast.val[0], env) + ";";
                                secd += '_func(' + args + ');';
                            }
                            return secd
                        }
                    } else {
                        secd += 'var _func=' + translate(ast.val[0], env) + ";";
                        var args = '';
                        for (var idx = 1; idx < ast.val.length; ++idx) {
                            secd += '_p_' + idx + "=" + translate(ast.val[idx], env) + ';';
                            args += '_p_' + idx
                            if (idx != ast.val.length - 1) {
                                args += ","
                            }
                        }
                        if (ast.istail) {
                            secd += 'return '
                        }
                        secd += '_func(' + args + ');';
                        return secd
                    }
                    break;
                case 'identify':
                    var seek = lookup(ast.val, env);
                    var secd = '';
                    if (seek) {
                        secd += '_v_' + seek[0] + "_" + seek[1];
                    } else {
                        secd += ast.val
                    }
                    return secd;
                    break;
                case 'quote':
                    break;
                default:
                    if (ast.istail) {
                        return 'return '+ast.val
                    }
                    return ast.val;
            }
        }
        return translate(ast);
    }
}

export { Jscheme };